package org.elastic.compreplatform.admin.core.shiro.token.manager;

import java.util.Date;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.elastic.compreplatform.admin.common.constant.LoginEnum;
import org.elastic.compreplatform.admin.core.shiro.session.CustomSessionManager;
import org.elastic.compreplatform.admin.core.shiro.token.ShiroToken;
import org.elastic.compreplatform.admin.core.shiro.token.SimpleRealm;
import org.elastic.compreplatform.admin.manager.sysuser.model.SysUser;
import org.elastic.compreplatform.common.constant.RespEnum;
import org.elastic.compreplatform.common.model.RespMsg;
import org.elastic.compreplatform.common.util.EncryptUtil;
import org.elastic.compreplatform.common.util.RespMsgUtil;
import org.elastic.compreplatform.common.util.SpringContextUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.minstone.quartz.core.util.IpUtil;

/**
 * ClassName: TokenManager 
 * @Description: Shiro管理下的Token工具类
 * @author JornTang
 * @date 2018年1月29日
 */
public class TokenManager {
	private static Logger log = LoggerFactory.getLogger(TokenManager.class);
	//用户登录管理
	public static final SimpleRealm realm = SpringContextUtil.getBean(SimpleRealm.class);
	//用户session管理
	public static final CustomSessionManager customSessionManager = SpringContextUtil.getBean(CustomSessionManager.class);
	/**
	 * 获取当前登录的用户User对象
	 * @return
	 */
	public static SysUser getCurrentUser() {
        SysUser user = (SysUser) SecurityUtils.getSubject().getPrincipal();
        return user;
    }
	
	/**
	 * 获取当前用户的Session
	 * @return
	 */
	public static Session getSession(){
		return SecurityUtils.getSubject().getSession();
	}
	/**
	 * 获取当前用户NAME
	 * @return
	 */
	public static String getNickname(){
		return getCurrentUser().getNickname();
	}
	/**
	 * 获取当前用户ID
	 * @return
	 */
	public static Long getUserId(){
		return getCurrentUser()==null?null:getCurrentUser().getId();
	}
	/**
	 * 把值放入到当前登录用户的Session里
	 * @param key
	 * @param value
	 */
	public static void setSessionValue(Object key ,Object value){
		getSession().setAttribute(key, value);
	}
	/**
	 * 从当前登录用户的Session里取值
	 * @param key
	 * @return
	 */
	public static Object getSessionValue(Object key){
		return getSession().getAttribute(key);
	}
	/**
	 * @Description: 获取验证码并删除
	 * @return   
	 * @return String  
	 * @throws
	 * @author JornTang
	 * @date 2018年1月29日
	 */
	public static String getCaptcha(){
		String captcha = (String) getSession().getAttribute(LoginEnum.CAPTCHA_CODE.getCode());
		getSession().removeAttribute(LoginEnum.CAPTCHA_CODE.getCode());
		return captcha ;
	}
	
	/**
	 * @Description: 登录验证
	 * @param username
	 * @param password
	 * @param code
	 * @param request
	 * @return   
	 * @return RespMsg  
	 * @throws
	 * @author JornTang
	 * @date 2018年1月28日
	 */
	@SuppressWarnings("finally")
	public static RespMsg loginCheck(String username, String password,String code, String rememberme, HttpServletRequest request) {
		log.info("Login verification start:{}", new Date());
        long start = System.currentTimeMillis();
        try {
        	//登录名、密码、验证码验证
        	RespMsg RespMsg = doCheck(username, password, code);
        	if(RespMsg != null){
        		return RespMsg;
        	};
        	//shiro 验证
        	ShiroToken token = new ShiroToken(username, EncryptUtil.getMD5(password));
            token.setRememberMe("on".equals(rememberme)?true:false);
            Subject currentUser = SecurityUtils.getSubject();

            currentUser.login(token);
            if (currentUser.isAuthenticated()) {
                SysUser user = getCurrentUser();
                // when login autumn-blog
                if (user.equals("02") && (user.getEmail() == null || user.getEmail().equals(""))){
                    return RespMsgUtil.returnMsg(RespEnum.HAVE_NOT_AUTH);
                }
                request.getSession().setAttribute(LoginEnum.LOGIN_SESSION_KEY.getCode(), user);

                //  record user login ip information
                try {
                	//此处记录登录日志
                	String ipAddr = IpUtil.getIpAddrs(request);
                    // 会抛出异常
                }catch (Exception e){
                    log.error("Invalid ip, exception{}", e);
                }finally {
                    return RespMsgUtil.returnMsg(RespEnum.GLOBAL_SUCCESS);
                }
            }
            return RespMsgUtil.returnMsg(RespEnum.GLOBAL_LOGIN_FAIL);
        } catch (IncorrectCredentialsException ice) {
            log.error("Login failed, reason:{}", "username and password can not match");
            return RespMsgUtil.returnMsg(RespEnum.GLOBAL_LOGIN_FAIL);
        }catch (Exception e) {
            log.error("Login failed, system exception：{}", e);
            return RespMsgUtil.returnMsg(RespEnum.GLOBAL_LOGIN_ERROR);
        } finally {
            log.info("Login verification end, spend {}ms", System.currentTimeMillis() - start);
        }
	}
	
	/**
	 * @Description: 验证
	 * @return   
	 * @return RespMsg  
	 * @throws
	 * @author JornTang
	 * @date 2018年1月29日
	 */
	private static RespMsg doCheck(String username, String password,String code){
		// username can not be null
        if (StringUtils.isEmpty(username)) {
            log.error("Login failed, reason:{}", RespEnum.GLOBAL_LOGIN_NAME_NULL.getMsg());
            return RespMsgUtil.returnMsg(RespEnum.GLOBAL_LOGIN_NAME_NULL);
        }
        // password can not be null
        if (StringUtils.isEmpty(password)) {
            log.error("Login failed, reason:{}", RespEnum.GLOBAL_LOGIN_PASS_NULL.getMsg());
            return RespMsgUtil.returnMsg(RespEnum.GLOBAL_LOGIN_PASS_NULL);
        }
        // code can not be null
        if (StringUtils.isEmpty(code)) {
            log.error("Login failed, reason:{}", RespEnum.GLOBAL_CAPTCHA_NULL.getMsg());
            return RespMsgUtil.returnMsg(RespEnum.GLOBAL_CAPTCHA_NULL);
        }
        // wrong code
        String sessionCode = TokenManager.getCaptcha();
        if(!code.toLowerCase().equals(sessionCode)) {
            log.error("Login failed, reason:{}[enter-code:{},session-code:{}]", RespEnum.GLOBAL_CAPTCHA_ERROR.getMsg(), code, sessionCode);
            return RespMsgUtil.returnMsg(RespEnum.GLOBAL_CAPTCHA_ERROR);
        }
        return null;
	}
	/**
	 * 登录
	 * @param user
	 * @param rememberMe
	 * @return
	 */
	public static SysUser login(SysUser user,Boolean rememberMe){
		ShiroToken token = new ShiroToken(user.getEmail(), user.getPswd());
		token.setRememberMe(rememberMe);
		SecurityUtils.getSubject().login(token);
		return getCurrentUser();
	}

	/**
	 * 判断是否登录
	 * @return
	 */
	public static boolean isLogin() {
		return null != SecurityUtils.getSubject().getPrincipal();
	}
	/**
	 * 退出登录
	 */
	public static void loginout() {
		SecurityUtils.getSubject().logout();
	}
	
	/**
	 * 清空当前用户权限信息。
	 * 目的：为了在判断权限的时候，再次会再次 <code>doGetAuthorizationInfo(...)  </code>方法。
	 * ps：	当然你可以手动调用  <code> doGetAuthorizationInfo(...)  </code>方法。
	 * 		这里只是说明下这个逻辑，当你清空了权限，<code> doGetAuthorizationInfo(...)  </code>就会被再次调用。
	 */
	public static void clearNowUserAuth(){
		/**
		 * 这里需要获取到shrio.xml 配置文件中，对Realm的实例化对象。才能调用到 Realm 父类的方法。
		 */
		/**
		 * 获取当前系统的Realm的实例化对象，方法一（通过 @link org.apache.shiro.web.mgt.DefaultWebSecurityManager 或者它的实现子类的{Collection<Realm> getRealms()}方法获取）。
		 * 获取到的时候是一个集合。Collection<Realm> 
			RealmSecurityManager securityManager =
		    			(RealmSecurityManager) SecurityUtils.getSecurityManager();
		  	SampleRealm realm = (SampleRealm)securityManager.getRealms().iterator().next();
		 */
		/**
		 * 方法二、通过ApplicationContext 从Spring容器里获取实列化对象。
		 */
		realm.clearCachedAuthorizationInfo();
		/**
		 * 当然还有很多直接或者间接的方法，此处不纠结。
		 */
	}
	
	
	
	
	/**
	 * 根据UserIds 	清空权限信息。
	 * @param id	用户ID
	 */
	public static void clearUserAuthByUserId(Long...userIds){
		
		if(null == userIds || userIds.length == 0)	return ;
		List<SimplePrincipalCollection> result = customSessionManager.getSimplePrincipalCollectionByUserId(userIds);
		
		for (SimplePrincipalCollection simplePrincipalCollection : result) {
			realm.clearCachedAuthorizationInfo(simplePrincipalCollection);
		}
	}


	/**
	 * 方法重载
	 * @param userIds
	 */
	public static void clearUserAuthByUserId(List<Long> userIds) {
		if(null == userIds || userIds.size() == 0){
			return ;
		}
		clearUserAuthByUserId(userIds.toArray(new Long[0]));
	}
}
